#include "fileCommand.h"

/**
 * 全局静态常量定义
 */
const char *storage_path = "/home/chen/storage";  // 仓库地址

int lsCommand(filePath_t* file_path,MYSQL* mysql,fileList_t* file_list) {
    char select_query[1024] = {0};
    sprintf(select_query,"select * from FileSystem where parent_id = %d and user_id = %d and is_deleted;",
        file_path->current_file_id,file_path->current_user_id);
    int query_ret = mysql_query(mysql,select_query);
    if(query_ret != 0) {
        return -1;
    }
    MYSQL_RES* res = mysql_store_result(mysql);
    MYSQL_ROW row;
    MYSQL_FIELD* field = mysql_fetch_field(res);
    int col_size = mysql_num_fields(res);
    int row_size = mysql_num_rows(res);
    file_list->file_number = row_size;
    int top = 0;    // 当前需要存入的文件项
    while((row = mysql_fetch_row(res)) != NULL) {
        for(int i = 0;i < col_size;++i) {
            if(strcmp(field[i].name,"file_name") == 0) {
                memset(file_list->file_elem[top].dir_name,0,sizeof(file_list->file_elem[top].dir_name));
                memcpy(file_list->file_elem[top].dir_name,row[i],strlen(row[i]));
            }
            if(strcmp(field[i].name,"file_category") == 0) {
                if(strcmp(row[i],"d") == 0) {
                    file_list->file_elem[top].dir_category = DIR_CATEGORY;
                }
                else {
                    file_list->file_elem[top].dir_category = FILE_CATEGORY;
                }
            }
            if(strcmp(field[i].name,"file_size") == 0) {
                file_list->file_elem[top].dir_size = atoi(row[i]);
            }
            if(strcmp(field[i].name,"last_active") == 0) {
                file_list->file_elem[top].last_modify_time = atol(row[i]);
            }
        }
        ++top;
    }
    return 0;
}
int cdCommand(filePath_t* file_path,MYSQL* mysql,const char* cd_path) {
    int current_cd_id = doStringTokenPath(file_path,mysql,cd_path);
    printf("获取当前目录id - %d\n",current_cd_id);
    // 1. 查询当前id对应的是否是目录
    char query[1024] = {0};
    MYSQL_RES* res;
    MYSQL_ROW row;
    sprintf(query,"select file_category from FileSystem where file_id = %d;",current_cd_id);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","cdCommand",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    if(mysql_num_rows(res) == 1) {
        row = mysql_fetch_row(res);
        if(strcmp(row[0],"d") != 0) {
            // 不是目录
            return -1;
        }
    }

    if(current_cd_id == -1) {
        printf("无法跳转\n");
        return -1;
    }
    file_path->current_file_id = current_cd_id;
    printf("跳转目录，当前目录 - %d\n",file_path->current_file_id);
    return 0;
}
int pwdCommand(filePath_t* file_path,MYSQL* mysql,char** pwd_string) {
    char pwd_path[1024] = {0};
    // 查询当前目录的目录路径,所属用户
    char user_dir_name[100] = {0};
    char user_dir_path[200] = {0};
    char query[1024] = {0};
    int query_ret;
    MYSQL_RES *res;
    MYSQL_ROW row;
    sprintf(query,"select file_name from FileSystem  \
        where user_id = %d and parent_id = -1 \
        and file_category = 'd' and is_deleted;",
        file_path->current_user_id);
    query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","pwdCommand",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    int size = mysql_num_rows(res);

    if(size == 1) {
        row = mysql_fetch_row(res);
        memcpy(user_dir_name,row[0],strlen(row[0]));
    }
    memset(query,0,1024);
    sprintf(query,"select file_path from FileSystem where file_id = %d and is_deleted;",file_path->current_file_id);
    query_ret = mysql_query(mysql,query);
    res = mysql_store_result(mysql);
    if(mysql_num_rows(res) == 1) {
        row = mysql_fetch_row(res);
        memcpy(user_dir_path,row[0],strlen(row[0]));
    }
    // 开始拼接
    strncpy(pwd_path,storage_path,strlen(storage_path));
    strcat(pwd_path,"/");
    strcat(pwd_path,user_dir_name);
    strcat(pwd_path,user_dir_path);
    memcpy(*pwd_string,pwd_path,strlen(pwd_path));

    return 0;
}
int mkdirCommand(filePath_t* file_path,MYSQL* mysql,const char* dir_path) {
    // 1. 获取需要跳转的路径，以及创建的路径名
    char dir_name[100] = {0};
    char dir_front_path[100] = {0};
    int last_dlim = 0;
    for(int i = strlen(dir_path) - 1;i >=0;--i) {
        if(dir_path[i] == '/') {
            last_dlim = i+1;
            break;
        }
    }
    for(int i = last_dlim;i < strlen(dir_path);++i) {
        dir_name[i - last_dlim] = dir_path[i];
    }
    for(int i = 0;i < last_dlim -1;++i) {
        dir_front_path[i] = dir_path[i];
    }
    int current_word_dir_id = file_path->current_file_id;
    if(strcmp(dir_name,"") == 0 || strcmp(dir_name,".") == 0 || strcmp(dir_name,"..") == 0) {
        // 校验目录名是否有效，一般是客户端实现
        printf("非有效目录名，无法创建\n");
        return -1;
    }
    if(strcmp(dir_front_path,"") != 0) {
        current_word_dir_id = doStringTokenPath(file_path,mysql,dir_front_path);
        if(current_word_dir_id == -1) {
            printf("%d : 目录不存在\n",__LINE__);
            return -1;
        }
    }
    printf("mkdir 已经获取当前的父目录id: %d\n",current_word_dir_id);
    MYSQL_RES* res;
    MYSQL_ROW row;
    // 2. 检查需要创建的目录是否可以直接恢复is_deleted状态
    int can_ret = canBeRestored(mysql,dir_name,current_word_dir_id,file_path->current_user_id,'d');
    if(can_ret == 0) {
        // 处理完成
        printf("mkdir 存在已删除的目录，恢复...\n");
        return 0;
    }
    // 3. 没有可恢复的目录，创建一个新目录
    // 查询需要创建的目录是否存在-同级下的同名文件
    // 加入用户目录下存在当前名称的目录,则不创建
    printf("mkdir 3 创建一个新的目录\n");
    char select_query[1024] = {0};
    sprintf(select_query,"select parent_id from FileSystem where file_name = '%s' and user_id = %d and is_deleted;",
        dir_name,file_path->current_user_id);
    int select_query_ret = mysql_query(mysql,select_query);
    if(select_query_ret != 0) {
        fprintf(stderr,"%s:%s\n","mkdirCommand",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);

    while((row = mysql_fetch_row(res)) != NULL) {
        if(current_word_dir_id== atoi(row[0])) {
            // 存在同名
            printf("当前目录下存在同名目录,无法创建\n");
            return -1;
        }
    }
    // 4. 获取父目录的file_path
    // 设置创建目录的file_path
    printf("mkdir 4 获取父目录的file_path\n");

    char current_file_path[1024] = {0};
    // 获取父目录的file_path
    char query[1024] = {0};
    sprintf(query,"select file_path from FileSystem where file_id = %d and is_deleted;",
        current_word_dir_id);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","mkdirCommand",mysql_error(mysql));
        return -1;
    }
    MYSQL_RES* msg_res = mysql_store_result(mysql);
    MYSQL_ROW msg_row;
    if(mysql_num_rows(msg_res) == 1) {
        msg_row = mysql_fetch_row(msg_res);

        if(msg_row[0] != NULL && strlen(msg_row[0]) != 0) {
            printf("父目录的路径 = %s\n",msg_row[0]);
            strcpy(current_file_path,msg_row[0]);
        }
    }
    // 拼接字符串 - /home/chen
    char *delim = "/";
    if(current_file_path[strlen(current_file_path) - 1] != '/') {
        strcat(current_file_path,delim);
    }
    strcat(current_file_path,dir_name);
    printf("mkdir 开始插入 - %s\n",dir_name);
    insertDirElem(mysql,dir_name,file_path->current_user_id,0,current_file_path,current_word_dir_id);
    
    return 0;
}
int rmdirCommand(filePath_t* file_path,MYSQL* mysql,const char* rm_dir_path) {
    // 1. 判断该目录是否为空
    // 2. 将该目录的is_deleted设置为false
    // ../work/
    int current_rmdir_id = doStringTokenPath(file_path,mysql,rm_dir_path);
    
    char query[1024] = {0};
    int query_ret;
    MYSQL_RES* res;
    // MYSQL_ROW row;
    // 跳转到当前路径的目录id
    memset(query,0,1024);
    sprintf(query,"select file_id from FileSystem where parent_id = %d and is_deleted;",current_rmdir_id);
    query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","rmdirCommand",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    int size = mysql_num_rows(res);
    if(size == 0) {
        // 目录为空
        // 更新is_deleted状态
        char update_query[1024] = {0};
        sprintf(update_query,"update FileSystem set is_deleted = false where file_id = %d;",current_rmdir_id);
        query_ret = mysql_query(mysql,update_query);
        if(query_ret != 0) {
            fprintf(stderr,"%s:%s\n","update rmdir status",mysql_error(mysql));
            return -1;
        }
    }else {
        printf("rmdir 目录不为空,无法删除\n");
        // 目录不为空
        return -1;
    }

    return 0;
}
int removeCommand(filePath_t* file_path,MYSQL* mysql,const char* rm_file_path) {
    // 1. 获取需要跳转的路径，以及创建的文件名
    char file_name[100] = {0};
    char dir_front_path[100] = {0};
    int last_dlim = 0;
    int current_dir_id;
    for(int i = strlen(rm_file_path) - 1;i >=0;--i) {
        if(rm_file_path[i] == '/') {
            last_dlim = i+1;
            break;
        }
    }
    for(int i = last_dlim;i < strlen(rm_file_path);++i) {
        file_name[i - last_dlim] = rm_file_path[i];
    }
    for(int i = 0;i < last_dlim -1;++i) {
        dir_front_path[i] = rm_file_path[i];
    }
    // 2. 获取当前父目录的路径
    if(strcmp(file_name,"") == 0 || strcmp(file_name,".") == 0 || strcmp(file_name,"..") == 0) {
        // 校验目录名是否有效，一般是客户端实现
        printf("非有效目录名，无法创建\n");
        return -1;
    }
    if(strcmp(dir_front_path,"") != 0) {
        current_dir_id = doStringTokenPath(file_path,mysql,dir_front_path);

        if(current_dir_id == -1) {
            printf("%d : 目录不存在\n",__LINE__);
            return -1;
        }
    }
    // 3. 查询当前文件是否存在
    int file_id;
    MYSQL_RES* res;
    MYSQL_ROW row;
    char query[4096] = {0};
    sprintf(query,"select file_id from FileSyetem \
        where parent_id = %d and file_category = 'f', \
        file_name = '%s' and user_id = %d;",
        current_dir_id,file_name,file_path->current_user_id);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"removeCommand : %s",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    int size = mysql_num_rows(res);
    if(size == 1) {
        // 查到了
        row = mysql_fetch_row(res);
        file_id  = atoi(row[0]);
    }else {
        // 出问题了
        return -1;
    }
    // 4. 更新文件状态
    char update_query[1024] = {0};
    sprintf(update_query,"update FileSystem set is_deleted = false where file_id = %d;",file_id);
    query_ret = mysql_query(mysql,update_query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","update rmdir status",mysql_error(mysql));
        return -1;
    }
    printf("文件已删除\n");
    return 0;
}

int doStringTokenPath(filePath_t* file_path,MYSQL* mysql,const char* path) {
    char rmdir_path[1024] = {0};
    memcpy(rmdir_path,path,strlen(path));
    const char* delim = "/";
    char* saveptr = NULL;
    char* token = NULL;
    token = strtok_r(rmdir_path,delim,&saveptr);

    int current_rmdir_id = file_path->current_file_id;

    char query[1024] = {0};
    int query_ret;
    MYSQL_RES* res;
    MYSQL_ROW row;
    while(token != NULL) {
        // 1. 当"."或""时,没有操作
        if(strcmp(token,".") == 0 || strcmp(token,"") == 0){}
        // 2. 当"..",查找parent_id
        else if(strcmp(token,"..") == 0) {
            memset(query,0,1024);
  
            sprintf(query,"select parent_id from FileSystem where file_id = %d and is_deleted;",
                current_rmdir_id);

            query_ret = mysql_query(mysql,query);
            if(query_ret != 0) {
                fprintf(stderr,"%s:%s\n","rmdirCommand",mysql_error(mysql));
                return -1;
            }
            res = mysql_store_result(mysql);
            if(res == NULL) {
                printf("res == NULL\n");
            }
            int size = mysql_num_rows(res);

            if(size == 1) {
                row = mysql_fetch_row(res);
                if(atoi(row[0]) != -1) {
                    current_rmdir_id = atoi(row[0]);
                }else {
                    // 访问越界
                    return -1;
                }
            }
            
        }
        else {
            memset(query,0,1024);
            sprintf(query,"select file_id from FileSystem where parent_id = %d \
                and user_id = %d and file_name = '%s' and file_category = 'd' and is_deleted;",
                current_rmdir_id,file_path->current_user_id,token);
            query_ret = mysql_query(mysql,query);
            if(query_ret != 0) {
                fprintf(stderr,"%s:%s\n","rmdirCommand",mysql_error(mysql));
                return -1;
            }
            res = mysql_store_result(mysql);
            int size = mysql_num_rows(res);
            if(size == 1) {
                row = mysql_fetch_row(res);
                current_rmdir_id = atoi(row[0]);
            }else if(size == 0) {
                printf("当前目录名：%s\n",token);
                printf("当前目录不存在\n");
                return -1;
            }else {
                printf("存在重复目录\n");
                return -1;
            }
        }
        token = strtok_r(NULL,delim,&saveptr);
    }
    return current_rmdir_id;
}

// int hashSha256(int file_fd,char* hash_name) {
//     char buf[4096] = {0};
//     unsigned char sha256[32] = {0};
//     SHA256_CTX ctx;
//     SHA256_Init(&ctx);
//     ssize_t read_size;
//     while((read_size = read(file_fd,buf,sizeof(buf))) != 0) {
//         SHA256_Update(&ctx,buf,read_size);
//         memset(buf,0,sizeof(buf));
//     }
//     SHA256_Final(sha256,&ctx);
//     for(int i = 0;i < 32;++i) {
//         char temp[3] = {0};
//         sprintf(temp,"%02x",sha256[i]);
//         strcat(hash_name,temp);
//     }
//     return 0;
// }

int touchCommand(filePath_t* file_path,MYSQL* mysql,const char* file_name,const char* hash_name,const int file_size) {
    MYSQL_RES* res;
    MYSQL_ROW row;
    // 1. 判断是否存在同名文件
    int restore_ret = canBeRestored(mysql,file_name,file_path->current_file_id,file_path->current_user_id,'f');
    if(restore_ret == 0) {
        // 存在同名文件
        return 0;
    }
    // 2. 获取当前文件路径
    char current_file_path[1024] = {0}; 
    char query[1024] = {0};
    sprintf(query,"select file_path from FileSystem where file_id = %d;",file_path->current_file_id);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","touchCommand",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    row = mysql_fetch_row(res);
    strcpy(current_file_path,row[0]);
    // 3. 插入数据库
    insertFileElem(mysql,file_name,file_path->current_user_id,hash_name,file_size,current_file_path,file_path->current_file_id);

    return 0;
}

int hashSha256(int file_fd,char* hash_name) {
    const EVP_MD* md = EVP_sha256();
    EVP_MD_CTX* ctx = EVP_MD_CTX_new();
    EVP_DigestInit_ex(ctx,md,NULL);
    ssize_t read_size;
    char buf[4096];

    while((read_size = read(file_fd,buf,sizeof(buf)) != 0)) {
        memset(buf,0,sizeof(buf));
        EVP_DigestUpdate(ctx,buf,read_size);
    }
    unsigned char sha256[32];
    unsigned int sha256_size = sizeof(sha256);
    EVP_DigestFinal_ex(ctx,sha256,&sha256_size);
    for(int i = 0;i < 32;++i) {
        char temp[3] = {0};
        sprintf(temp,"%02x",sha256[i]);
        strcat(hash_name,temp);
    }
    EVP_MD_CTX_free(ctx);
    return 0;
}

int canBeRestored(MYSQL* mysql,const char* file_name,const int file_id,const int user_id,const char file_category) {
    char is_deleted_query[1024] = {0};
    printf("file_name = %s\n",file_name);
    sprintf(is_deleted_query,"select * from FileSystem \
        where file_name = '%s' and parent_id = %d and \
        user_id = %d and file_category = '%c';",
        file_name,file_id,user_id,file_category);
    MYSQL_RES * res;
    MYSQL_ROW row;
    int is_deleted_query_ret = mysql_query(mysql,is_deleted_query);
    if(is_deleted_query_ret != 0) {
        fprintf(stderr,"%s:%s\n","mkdirCommand select",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    int nums_size = mysql_num_rows(res);
    if(nums_size != 0) {    // 存在被删除的相同目录
        MYSQL_FIELD * field = mysql_fetch_field(res);
        if(nums_size > 1) {
            // 程序出问题了，创建了多个同名目录
            return -1;
        }
        row = mysql_fetch_row(res);
        int have_file_id = atoi(row[0]);
        printf("已存在目录的id = %d\n",have_file_id);
        for(int i = 0;i < mysql_num_fields(res);++i) {
            if(strcmp(field[i].name,"is_deleted") == 0) {
                if(atoi(row[i]) == 0) {
                    // 更新该条目录
                    time_t update_time = time(NULL);
                    char update_query[1024] = {0};
                    sprintf(update_query,"update FileSystem set is_deleted = true, \
                        last_active = %ld where file_id = %d;",
                        update_time,have_file_id);
                    int update_query_ret = mysql_query(mysql,update_query);
                    if(update_query_ret != 0) {
                        fprintf(stderr,"%s:%s\n","mkdirCommand update",mysql_error(mysql));
                        return -1;
                    }  
                    return 0;
                }
            }
        }
    }
    return -1;
}

int isFileExist(MYSQL* mysql,const char* hash_name) {
    char* result = (char*)calloc(4096,sizeof(char));
    char where_query[1024] = {0};
    sprintf(where_query,"(file_hash = '%s')",hash_name);
    selectField(mysql,"FileSystem","file_id",where_query,&result);
    int file_id = atoi(result);
    memset(result,0,4096);
    free(result);
    if(file_id > 0) {
        return 0;
    } 
    return -1;
}
int isFileCompleted(MYSQL* mysql,filePath_t file_path,const char* file_name) {
    char query[1024] = {0};
    sprintf(query,"select file_id from FileSystem \
        where file_name = '%s' and user_id = %d and parent_id = %d and not completed_file;",
        file_name,file_path.current_user_id,file_path.current_file_id);

    MYSQL_RES* res;
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","isFileCompleted",mysql_error(mysql));
        return -1;
    }
    res = mysql_store_result(mysql);
    
    int size = mysql_num_rows(res);
    if(size == 1) {
        return 0;
    }
    return -1;
}
int updateFileCompleted(MYSQL* mysql,filePath_t file_path,const char* file_name,const char* new_hash_name) {
    char query[1024] = {0};
    sprintf(query,"update FileSystem set completed_file = true,file_hash = '%s' \
        where user_id = %d and file_name = '%s' and parent_id = %d and not completed_file;",
        new_hash_name,file_path.current_user_id,file_name,file_path.current_file_id);
    
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","isFileCompleted",mysql_error(mysql));
        return -1;
    }
    return 0;
}

int insertTempFileElem(MYSQL* mysql,filePath_t file_path,const char* file_name,const int file_size,const char* file_hash) {
    char query[4096] = {0};
    time_t new_time = time(NULL);
    char *current_file_path = (char*)calloc(4096,sizeof(char));
    getFilePathByFileID(mysql,file_path.current_file_id,&current_file_path);
    sprintf(query,"insert into FileSystem values(default,'%s',%d,'f','%s',%d,%ld,%s,%d,false,default);",
        file_name,file_path.current_user_id,file_hash,file_size,new_time,current_file_path,file_path.current_file_id);
    
    memset(current_file_path,0,4096);
    free(current_file_path);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","insertTempFileElem",mysql_error(mysql));
        return -1;
    }
    return 0;
}

int insertCompFileElem(MYSQL* mysql,filePath_t file_path,const char* file_name,const int file_size,const char* file_hash) {
    char query[4096] = {0};
    time_t new_time = time(NULL);
    char *current_file_path = (char*)calloc(4096,sizeof(char));
    getFilePathByFileID(mysql,file_path.current_file_id,&current_file_path);
    sprintf(query,"insert into FileSystem values(default,'%s',%d,'f','%s',%d,%ld,%s,%d,true,default);",
        file_name,file_path.current_user_id,file_hash,file_size,new_time,current_file_path,file_path.current_file_id);
    
    memset(current_file_path,0,4096);
    free(current_file_path);
    int query_ret = mysql_query(mysql,query);
    if(query_ret != 0) {
        fprintf(stderr,"%s:%s\n","insertTempFileElem",mysql_error(mysql));
        return -1;
    }
    return 0;
}